﻿//-------------------------------------
// WBFSSync - WBFSSync.exe
//
// Copyright 2009 Caian (ÔmΣga Frøst) <frost.omega@hotmail.com> :
//
// WBFSSync is Licensed under the terms of the Microsoft Reciprocal License (Ms-RL)
//
// FormWBFSSync.cs:
//
// Janela principal do programa
//
// Nota:
//
//  'Discos válidos' ou as vezes apenas 'Discos' são: Isos de Wii, Scrubs e Rars com pelo menos 1 iso de Wii
//
//  Atalhos:
//
//   -F2       : Abre painel para renomear arquivos (1 por vez)
//   -Del      : Apaga um ou mais arquivos, Capas ou Pastas da Biblioteca
//   -Ctrl + C : Copia um ou mais Discos
//   -Ctrl + X : Recorta um ou mais Discos
//   -Ctrl + V : Cola os Discos copiados do programa ou da Clipboard, aquele que veio por último.
//   -Ctrl + N : Nova pasta para a biblioteca
//   -Ctrl + E : Expora as capas usando o primeiro esquema de LoaderHomebrew da lista
//
//-------------------------------------

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using WBFSSync.AeroControls;
using System.Security.AccessControl;
using System.Security.Principal;
using WBFSSync.Properties;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace WBFSSync
{
    public partial class FormWBFSSync : Form
    {
        //----------------- Constantes

        const bool isBeta = true;

        public const string typefilter_rar = "*.rar";

        public const string path_computer = "#COMPUTER";
        public const string path_covers = "#COVERS";
        public const string path_library = "#GAMES";

        public delegate void VoidVoidDelegate();
        public delegate void VoidStringDelegate(string s);
        public delegate void VoidStringLogMessageTypeDelegate(string s, LogMessageType t);
        public delegate void VoidOperationDelegate(Operation o);

        //----------------- Variáveis

        Aero aero;

        String explorerPath = path_computer;
        String ExplorerPath { get { return explorerPath; } set { explorerPath = value; OnPathChanged(); } }

        public static Boolean EnablePathChangeSound = true;
        System.Media.SoundPlayer PathChangeSound = null;

        Int32 SelectedItemsCount { get { return listView_explorer.SelectedItems.Count; } }
        Int32 LastSelectedItemsCount = 0;
        SelectionType SelectedType = SelectionType.Invalid;

        Boolean SuspendDeviceAndLibraryEvents; //Durante a inicialização do programa apenas, impede várias chamadas
                                               //repetidas de atualização de dispositivos e livrarias

        Int32 LogLineCount = 0;

        ListViewGroup FolderGroup = null;
        ListViewGroup WiiDiscGroup = null;
        ListViewGroup ScrubbedDiscGroup = null;
        ListViewGroup CIsoDiscGroup = null;
        ListViewGroup FixedDriveGroup = null;
        ListViewGroup RemovableGroup = null;
        ListViewGroup NetworkGroup = null;
        ListViewGroup WBFSGroup = null;
        ListViewGroup UnknownGroup = null;

        ConfigurationReader Configuation = new ConfigurationReader();

        //----------------- Rotinas

        //----------------- Construtor da classe
        public FormWBFSSync()
        {
            //----- Expande o aplicativo para todos os cores possíveis
            ThreadManager.SetProcessAfinityToAllCores();

            //----- Inicia o Monitoramento da Clipboard
            OperationManager.StartClipboardMonitoring(this.Handle);

            //----- Inicia as fontes da Skin
            Skin.LoadFonts();

            //----- Inicia os componentes da janela
            InitializeComponent();
            this.Text = Language.AppName;
            explorerListViewHeader_explorer.AssociateWithListView(listView_explorer);

            //----- Associa eventos
            DeviceManager.DeviceListChanged += new DeviceListChangedDelegate(DeviceManager_DeviceListChanged);
            Library.LibraryChanged += new LibraryChangedDelegate(Libraries_LibraryChanged);
            Covers.CoverListChanged += new EventHandler(Covers_CoverListChanged);

            OperationManager.OperationCreated += new OperationEventDelegate(OperationManager_OperationStart);
            OperationManager.OperationEnd += new OperationEventDelegate(OperationManager_OperationEnd);
            OperationManager.OperationActivated += new OperationEventDelegate(OperationManager_OperationActivated);
            OperationManager.ClipboardChanged += new ClipboardChangedDelegate(OperationManager_ClipboardChanged);

            quickLinksPanel_quicklinks.QuickLinkClick += new QuickLinkClickDelegate(quickLinksPanel_quicklinks_QuickLinkClick);
            quickLinksPanel_quicklinks.QuickLinkSpecialClick += new QuickLinkClickDelegate(quickLinksPanel_quicklinks_QuickLinkSpecialClick);
            listView_explorer.ViewModeChanged += new EventHandler(listView_explorer_ViewModeChanged);
            pathBox_path.PathClick += new PathEvent(pathBox_path_PathClick);
            pathBox_path.RefreshClick += new PathEvent(pathBox_path_RefreshClick);
            configurationPanel_configuration.VisibleChanged += new EventHandler(configurationPanel_configuration_VisibleChanged);

            Log.AppendLogLine += new AppendLogLineDelegate(Log_AppendLogLine);
            Log.LastLineChanged += new LastLineChangedDelegate(Log_LastLineChanged);
            Log.ObjectMessageSent += new MessageSentDeletate(Log_ObjectMessageSent);

            //----- Inicia a transparência
            aero = new Aero(this);
        }

        void configurationPanel_configuration_VisibleChanged(object sender, EventArgs e)
        {
            if (!configurationPanel_configuration.Visible)
            {
                label_empty_folder.Visible = (listView_explorer.Items.Count == 0);
                SetupCoversMenuContext();
            }

            if (EnablePathChangeSound)
                PathChangeSound.Play();
        }

        //----------------- Overrides

        //----------------- Override da rotina de processamento de mensagens
        protected override void WndProc(ref Message m)
        {

            DeviceManager.ProcessSystemMessage(m.Msg, m.WParam.ToInt64(), m.LParam);
            OperationManager.ProcessSystemMessage(m.Msg, m.WParam, m.LParam);

            base.WndProc(ref m);
        }

        //----------------- Override da rotina de carregamento do Form
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            //
            //----- Suspende a atualização de alguns componentes para poupar processamento
            SuspendDeviceAndLibraryEvents = true;
            listView_log.SuspendLayout();

            //----- Loga o nome e a versão do programa
            Log.LogLine(FormatApplicationName(), LogMessageType.Message);

            //----- Carrega as configurações
            Log.LogLine(Language.log_load_config, LogMessageType.Message);
            LoadConfiguration();

            //----- Enumera os Skins e Idiomas
            Language.EnumerateLanguages();
            Skin.EnumerateSkins();

            //----- Inicia idiomas antes de tudo para ser usado pelo Log, se o idioma não existir o padrão
            //Pt-Br é usado
            Log.LogLine(Language.log_load_lang, LogMessageType.Message, Language.SelectedLanguage);
            if (!Language.Load(Language.SelectedLanguage))
            {
                Language.SelectedLanguage = Language.DefaultLanguage;
                Log.LogLine(Language.log_load_lang_fail, LogMessageType.Warning);
            }

            //----- Mapeia os drives
            Log.LogLine(Language.log_map_devices, LogMessageType.Message);
            DeviceManager.MapAllDevices();
            
            //Drives virtuais, apenas para debug...
            string vdrive;
            int order = 0;
            while (Configuation.GetStringArg("VDrive", order++, out vdrive)) DeviceManager.AddVirtualDevice(vdrive);

            Log.LogLine(Language.log_devices_found, LogMessageType.Message, DeviceManager.DriveCount, 
                DeviceManager.WBFSDriveCount);

            //----- Carrega as bibliotecas
            Log.LogLine(Language.log_load_libraries, LogMessageType.Message);
            /*Libraries.AddFolder*/
            Log.LogLine(Language.log_libraries_found, LogMessageType.Message, Library.Files.Count, Library.Folders.Count);

            //----- Carrega as capas
            Log.LogLine(Language.log_load_covers, LogMessageType.Message);
            Covers.Initialize();
            Log.LogLine(Language.log_covers_found, LogMessageType.Message, Covers.AvaliableCovers.Count);

            //----- Inicia a skin
            Log.LogLine(Language.log_load_skin, LogMessageType.Message, Skin.SelectedSkin);
            if (!Skin.LoadSkin(Skin.SelectedSkin))
            {
                Log.LogLine(Language.log_load_skin_fail, LogMessageType.Warning, Skin.SelectedSkin, Skin.DefaultSkin);
                if (!Skin.LoadSkin(Skin.DefaultSkin))
                {
                    Log.LogLine(Language.log_load_skin_criticalfail, LogMessageType.Error);
                    throw new ArgumentException(Language.exception_defaultskinnotfound, Skin.FailedSkinFile);
                }
            }

            //----- Aplica o Skin e idioma em alguns componentes

            Log.LogLine(Language.log_applying_skin, LogMessageType.Message);

            //----- Configurações

            configurationPanel_configuration.SetLanguage();

            //----- Menu principal

            panel_menu.BackgroundImage = Skin.menu_background;
            pictureBox_menu_separator_1.Image = Skin.menu_separator;
            pictureBox_menu_separator_2.Image = Skin.menu_separator;

            aeroMenuButton_organize.Caption = Language.menuitem_organize;

            aeroMenuButton_cut.Caption = Language.menuitem_cut;
            aeroMenuButton_copy.Caption = Language.menuitem_copy;
            aeroMenuButton_paste.Caption = Language.menuitem_paste;
            aeroMenuButton_rename.Caption = Language.menuitem_rename;
            aeroMenuButton_delete.Caption = Language.menuitem_delete;
            aeroMenuButton_compress.Caption = Language.menuitem_compress;
            aeroMenuButton_decompress.Caption = Language.menuitem_decompress;

            aeroMenuButton_format.Caption = Language.menuitem_format;            
            aeroMenuButton_viewmode.Icon = Skin.icon_16x16_view_tile;
            aeroMenuButton_config.Icon = Skin.icon_16x16_config;
            aeroMenuButton_donate.Icon = Skin.icon_16x16_donate;
            aeroMenuButton_help.Icon = Skin.icon_16x16_help;

            aeroMenuButton_organize.ToolTipText = Language.tooltip_organize;

            aeroMenuButton_viewmode.ToolTipText = Language.tooltip_viewmode;
            aeroMenuButton_config.ToolTipText = Language.tooltip_config;
            aeroMenuButton_donate.ToolTipText = Language.tooltip_donate;
            aeroMenuButton_help.ToolTipText = Language.tooltip_help;

            panel_menu.AddControlLeft(aeroMenuButton_organize);
            panel_menu.AddControlLeft(pictureBox_menu_separator_2);

            panel_menu.AddControlLeft(aeroMenuButton_cut);
            panel_menu.AddControlLeft(aeroMenuButton_copy);
            panel_menu.AddControlLeft(aeroMenuButton_paste);

            panel_menu.AddControlLeft(pictureBox_menu_separator_4);

            panel_menu.AddControlLeft(aeroMenuButton_delete);
            panel_menu.AddControlLeft(aeroMenuButton_rename);

            panel_menu.AddControlLeft(pictureBox_menu_separator_3);

            panel_menu.AddControlLeft(aeroMenuButton_compress);
            panel_menu.AddControlLeft(aeroMenuButton_decompress);

            panel_menu.AddControlRight(aeroMenuButton_help);
            panel_menu.AddControlRight(aeroMenuButton_donate);
            panel_menu.AddControlRight(aeroMenuButton_config);
            panel_menu.AddControlRight(aeroMenuButton_viewmode);

            panel_menu.AddControlRight(pictureBox_menu_separator_1);

            panel_menu.AddControlRight(aeroMenuButton_format);

            panel_menu.OrganizeControls();

            //----- Menu organização

            toolStripMenuItem_organize_cut.Text = Language.menuitem_cut;
            toolStripMenuItem_organize_copy.Text = Language.menuitem_copy;
            toolStripMenuItem_organize_paste.Text = Language.menuitem_paste;
            toolStripMenuItem_organize_delete.Text = Language.menuitem_delete;
            toolStripMenuItem_organize_rename.Text = Language.menuitem_rename;
            toolStripMenuItem_organize_compress.Text = Language.menuitem_compress;
            toolStripMenuItem_organize_decompress.Text = Language.menuitem_decompress;
            toolStripMenuItem_organize_covers.Text = Language.foldername_covers;
            toolStripMenuItem_organize_library.Text = Language.foldername_library;
            toolStripMenuItem_organize_lib_add.Text = Language.menuitem_newfoldertolib;
            toolStripMenuItem_organize_lib_clear.Text = Language.menuitem_clearlib;
            toolStripMenuItem_organize_config.Text = Language.menuitem_config;
            toolStripMenuItem_organize_about.Text = Language.menuitem_about;
            toolStripMenuItem_organize_donate.Text = Language.menuitem_donate;
            toolStripMenuItem_organize_help.Text = Language.menuitem_help;
            toolStripMenuItem_organize_close.Text = Language.menuitem_close;
            
            toolStripMenuItem_organize_cut.Image = Skin.icon_16x16_cut;
            toolStripMenuItem_organize_copy.Image = Skin.icon_16x16_copy;
            toolStripMenuItem_organize_paste.Image = Skin.icon_16x16_paste;
            toolStripMenuItem_organize_delete.Image = Skin.icon_16x16_delete;

            //----- Menu viewmode

            toolStripMenuItem_viewmode_cover.Text = Language.menuitem_viewmode_cover;
            toolStripMenuItem_viewmode_tile.Text = Language.menuitem_viewmode_tile;
            toolStripMenuItem_viewmode_list.Text = Language.menuitem_viewmode_list;
            toolStripMenuItem_viewmode_details.Text = Language.menuitem_viewmode_details;

            toolStripMenuItem_viewmode_cover.Image = Skin.icon_16x16_view_cover;
            toolStripMenuItem_viewmode_tile.Image = Skin.icon_16x16_view_tile;
            toolStripMenuItem_viewmode_list.Image = Skin.icon_16x16_view_list;
            toolStripMenuItem_viewmode_details.Image = Skin.icon_16x16_view_details;

            //----- Menu explorer

            toolStripMenuItem_explorer_cut.Text = Language.menuitem_cut;
            toolStripMenuItem_explorer_copy.Text = Language.menuitem_copy;
            toolStripMenuItem_explorer_paste.Text = Language.menuitem_paste;

            toolStripMenuItem_explorer_rename.Text = Language.menuitem_rename;
            toolStripMenuItem_explorer_delete.Text = Language.menuitem_delete;

            toolStripMenuItem_explorer_compress.Text = Language.menuitem_compress;
            toolStripMenuItem_explorer_decompress.Text = Language.menuitem_decompress;

            toolStripMenuItem_explorer_sendto.Text = Language.menuitem_sendto;

            toolStripMenuItem_explorer_downloadcover.Text = Language.menuitem_downloadcover;

            //----- Guias

            tab_operations.Icon = Skin.icon_16x16_operations;
            tab_log.Icon = Skin.icon_16x16_log;
            tab_clipboard.Icon = Skin.icon_16x16_clipboard;

            tab_operations.Caption = Language.tab_operations + " (0/0)";
            tab_log.Caption = Language.tab_log + " (" + LogLineCount + ')';
            tab_clipboard.Caption = Language.tab_clipboard + " (0)";

            tab_operations.Width = tab_operations.CaptionTextWidth + 63;
            tab_log.Width = tab_log.CaptionTextWidth + 48;
            tab_clipboard.Width = tab_clipboard.CaptionTextWidth + 48;

            tab_log.Left = tab_operations.Right - 1;
            tab_clipboard.Left = tab_log.Right - 1;

            //----- Colunas do Explorer

            listView_explorer.Columns[0].Text = Language.explorer_header_name;
            listView_explorer.Columns[1].Text = Language.explorer_header_typecode;
            listView_explorer.Columns[2].Text = Language.explorer_header_region;
            listView_explorer.Columns[3].Text = Language.explorer_header_size;

            //----- Aviso de pasta vazia do explorer

            label_empty_folder.Text = Language.explorer_emptyfolder;
            label_empty_folder.Font = Skin.IconTitleFont;

            //----- Colunas da lista de operações

            listView_operations.Columns[0].Text = Language.operations_header_operation;
            listView_operations.Columns[1].Text = Language.operations_header_source;
            listView_operations.Columns[2].Text = Language.operations_header_destination;
            listView_operations.Columns[3].Text = Language.operations_header_progress;

            //----- Som de mudança de pasta do explorer

            PathChangeSound = new System.Media.SoundPlayer(Skin.explorer_double_click_sound);
            PathChangeSound.Load();

            //----- Cria os grupos da listview

            FolderGroup = new ListViewGroup("FOLDER", Language.explorer_folder);
            WiiDiscGroup = new ListViewGroup("WIIDISC", Language.explorer_wiidisc);
            ScrubbedDiscGroup = new ListViewGroup("SCRUBDISC", Language.explorer_scrubbeddisc);
            CIsoDiscGroup = new ListViewGroup("CISODISC", Language.explorer_cisodisc);
            FixedDriveGroup = new ListViewGroup("FIXED", Language.explorer_fixeddrives);
            RemovableGroup = new ListViewGroup("REMOVABLE", Language.explorer_removabledrives);
            NetworkGroup = new ListViewGroup("NETWORK", Language.explorer_networkdrives);
            WBFSGroup = new ListViewGroup("WBFS", Language.explorer_wbfsdrives);
            UnknownGroup = new ListViewGroup("UNKNOWN", Language.drivename_unknown);

            listView_explorer.Groups.Add(WiiDiscGroup);
            listView_explorer.Groups.Add(ScrubbedDiscGroup);
            listView_explorer.Groups.Add(FolderGroup);
            listView_explorer.Groups.Add(FixedDriveGroup);
            listView_explorer.Groups.Add(WBFSGroup);
            listView_explorer.Groups.Add(RemovableGroup);
            listView_explorer.Groups.Add(NetworkGroup);

            //----- Cria o menu de contexto das capas

            SetupCoversMenuContext();

            SuspendDeviceAndLibraryEvents = false;
            listView_log.ResumeLayout();

            //----- Sincroniza capas

            Log.LogLine(Language.log_synccovers, LogMessageType.Message);
            int newcovers = Covers.SynchronizeCovers();
            if (newcovers > 0) Log.LogLine(Language.log_new_covers_found, LogMessageType.Message, newcovers);

            Log.LogLine(Language.log_done, LogMessageType.Message);

            //----- Verifica atualizações

            if ((ProgramUpdate.UpdateMode == UpdateMode.OnExecute) ||
               ((ProgramUpdate.UpdateMode == UpdateMode.OnceADay) && !ProgramUpdate.UpdatedToday) ||
               ((ProgramUpdate.UpdateMode == UpdateMode.OnceAWeek) && !ProgramUpdate.UpdatedThisWeek))
            {
                OperationManager.UpdateProgram();
            }

            //----- Verifica a área de transferência do windows
            OperationManager.UpdateClipboard();

            //----- Atualiza os atalhos
            SetupQuickLinks();

            //----- Atualiza o menu Enviar Para
            SetupSendToMenuContext();

            //----- Carrega a pagina 'Computador' como padrão
            LoadComputer();
        }

        //----------------- Override da rotina executada antes do Form fechar
        protected override void OnFormClosing(FormClosingEventArgs e)
        {
            //----- Atualiza as configurações

            if (configurationPanel_configuration.Visible)
            {
                DialogResult r = MessageBox.Show(this, Language.message_apply_config_before_exit, Language.AppName,
                    MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1);

                switch (r)
                {
                    case DialogResult.Cancel:
                        e.Cancel = true;
                        return;

                    case DialogResult.Yes:
                        configurationPanel_configuration.Apply();
                        break;
                }
            }

            UpdateConfiguration();

            //----- 

            if ((OperationManager.TotalOperations > 0) || (OperationManager.ActiveOperations > 0))
            {
                if (MessageBox.Show(this, Language.message_close_operations, Language.AppName, MessageBoxButtons.YesNo,
                    MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) == DialogResult.No)
                {
                    e.Cancel = true;
                    return;
                }
            }

            //----- Descarrega todas as capas
            Covers.DisposeCovers();
            Covers.DisposeFullSizeCovers();

            //----- Para o Monitoramento da Clipboard
            OperationManager.StopClipboardMonitoring();
            OperationManager.StopAllOperations();

            //----- Fecha todos os dispositivos
            DeviceManager.CloseAllDevices();

            //----- Fecha todos os Handles
            IOManager.Dispose();

            //----- Limpa a livraria
            Library.Clear();

            base.OnFormClosing(e);
        }

        //------------------------ Trata alguns atalhos globais ao invés de criar um menu invisível só para isso
        protected override void OnKeyDown(KeyEventArgs e)
        {
            if (e.Modifiers == Keys.Control)
            {
                if (e.KeyValue == (int)Keys.N)
                {
                    //Adicionar pasta à Biblioteca

                    if (ShowFolderBrowseDialog(Language.field_dialog_name_add_to_library, Environment.SpecialFolder.Desktop) ==
                        DialogResult.OK)
                    {
                        Library.AddFolder(folderBrowserDialog_main.SelectedPath);
                    }
                }
                else if (e.KeyValue == (int)Keys.E)
                {
                    if (Covers.Loaders.Count == 0) return;

                    int loader = 0;
                    String basepath = Covers.Loaders[loader].Path;

                    if (Covers.LookForCompatiblePath)
                    {
                        IDrive drive = null;
                        int i = 0;
                        while ((drive = DeviceManager.GetDrive(i++)) != null)
                        {
                            if ((!drive.Ready) || (drive.Is(DriveFlags.WBFS))) continue;
                            if (Directory.Exists(drive.Name + basepath))
                            {
                                OperationManager.ExportCovers(loader, drive.Name);
                                return;
                            }
                        }
                    }

                    //Não achou diretório compatível ou Covers.LookForCompatiblePath = false
                    if (ShowFolderBrowseDialog(Language.message_browsedrive, Environment.SpecialFolder.MyComputer) ==
                        DialogResult.OK)
                    {
                        OperationManager.ExportCovers(loader, folderBrowserDialog_main.SelectedPath + '\\');
                    }
                }
            }

            base.OnKeyDown(e);
        }

        //------------------------ Formata o nome do programa
        string FormatApplicationName()
        {
            return String.Format("{0} {1}{2}", Language.AppName, Application.ProductVersion, isBeta ? 'b' : 'r');
        }

        //------------------------ Converte um tipo de drive para texto
        public static string FormatDriveType(IDrive d)
        {
            if (d.Is(DriveFlags.WBFS))
            {
                return Language.drivename_wbfs;
            }
            else if (d.Is(DriveFlags.Removable))
            {
                return Language.drivename_removable;
            }
            else if (d.Is(DriveFlags.Fixed))
            {
                return Language.drivename_fixed;
            }
            else if (d.Is(DriveFlags.CDRom))
            {
                return Language.drivename_optical;
            }
            else
            {
                return Language.drivename_unknown;
            }
        }

        //------------------------ Formata o nome do drive a partir do seu tipo
        public static string FormatDriveName(IDrive d)
        {
            if (d.Label.Length > 0)
            {
                return d.Label + " (" + d.Name[0] + ":)";
            }
            else
            {
                if (d.Is(DriveFlags.WBFS))
                {
                    return Language.drivename_wbfs + " (" + d.Name[0] + ":)";
                }
                else if (d.Is(DriveFlags.Removable))
                {
                    return Language.drivename_removable + " (" + d.Name[0] + ":)";
                }
                else if (d.Is(DriveFlags.Fixed))
                {
                    return Language.drivename_fixed + " (" + d.Name[0] + ":)";
                }
                else if (d.Is(DriveFlags.CDRom))
                {
                    return Language.drivename_optical + " (" + d.Name[0] + ":)";
                }
                else if (d.Is(DriveFlags.Network))
                {
                    String netpath = DeviceManager.GetDrivePath(d.Name);
                    int cnamebreak = netpath.IndexOf('\\', 2);
                    int lastbreak = netpath.LastIndexOf('\\');

                    return String.Format("{0} ({1}) ({2}:)", netpath.Substring(lastbreak + 1),
                        netpath.Substring(0, cnamebreak), d.Name[0]);
                }
                else
                {
                    return d.Name[0] + ":";
                }
            }
        }

        //------------------------ Formata uma tamanho em bytes para KB, MB, GB
        public static string FormatFileSize(long size)
        {
            return FormatFileSize(size, 2);
        }

        public static string FormatFileSize(long size, int gigprecision)
        {
            String ssize;
            if (size < 1000)
                ssize = size + " Bytes";
            else if (size < 1000000)
                ssize = ((double)size / 1000.0f).ToString("F01") + " KB ";
            else if (size < 1000000000)
                ssize = ((double)size / 1000000.0f).ToString("F01") + " MB ";
            else
                ssize = ((double)size / 1000000000.0f).ToString("F0" + gigprecision) + " GB ";

            return ssize;
        }

        //------------------------ Formata uma tamanho em bytes para KiB, MiB, GB
        public static string FormatFileSizeEbi(long size)
        {
            return FormatFileSizeEbi(size, 2);
        }

        public static string FormatFileSizeEbi(long size, int gigprecision)
        {
            String ssize;
            if (size < 1024)
                ssize = size + " Bytes";
            else if (size < 1048576)
                ssize = ((double)size / 1024.0f).ToString("F01") + " KB ";
            else if (size < 1073741824)
                ssize = ((double)size / 1048576.0f).ToString("F01") + " MB ";
            else
                ssize = ((double)size / 1073741824.0f).ToString("F0" + gigprecision) + " GB ";

            return ssize;
        }

        //------------------------ Converte uma região de disco para texto
        string FormatDiscRegion(int region)
        {
            switch (region)
            {
                case 0: return "NTSC-J";
                case 1: return "NTSC-U";
                case 2: return "PAL";
                case 4: return "KOR";
                default: return Language.disc_region_unknown;
            }
        }

        //------------------------ Carrega as configurações do programa
        private void LoadConfiguration()
        {
            if (!Configuation.OpenConfigurationFile("wbfssync.ini"))
            {
                File.WriteAllText("wbfssync.ini", Resources.defaultconfig);
                Configuation.OpenConfigurationFile("wbfssync.ini");
            }

            String s;
            Int32 i;
            Int32 j;
            Int64 l;
            Boolean b;

            //Skin

            if (Configuation.GetStringArg("Skin", 0, out s)) Skin.SelectedSkin = s;
            if (Configuation.GetStringArg("Skin.Folder", 0, out s)) Skin.SkinFolder = s;

            //Idioma

            if (Configuation.GetStringArg("Language", 0, out s)) Language.SelectedLanguage = s;
            if (Configuation.GetStringArg("Language.Folder", 0, out s)) Language.LanguageFolder = s;

            //Janela Principal

            if (Configuation.GetInt32Arg("Window.X", 0, out i)) { if (i >= 0) Left = i; }
            if (Configuation.GetInt32Arg("Window.Y", 0, out j)) { if (j >= 0) Top = j; }

            if ((i < 0) && (j < 0)) StartPosition = FormStartPosition.WindowsDefaultLocation;
            else StartPosition = FormStartPosition.Manual;

            if (Configuation.GetBoolArg("Window.Maximized", 0, out b)) { if (b) { WindowState = FormWindowState.Maximized; } }

            if (Configuation.GetInt32Arg("Window.W", 0, out i)) { if (i > 0) Width = i; }
            if (Configuation.GetInt32Arg("Window.H", 0, out i)) { if (i > 0) Height = i; }

            if (Configuation.GetInt32Arg("VSplit.X", 0, out i)) { if (i > 0) aeroSplitContainer_vertical.SplitterDistance = i; }
            if (Configuation.GetInt32Arg("HSplit.Y", 0, out i)) { if (i > 0) aeroSplitContainer_horizontal.SplitterDistance = i; }

            //Lista de operações

            if (Configuation.GetInt32Arg("OPList.Operation.W", 0, out i)) { if (i > 0) { columnHeader_operation.Width = i; } }
            if (Configuation.GetInt32Arg("OPList.Source.W", 0, out i)) { if (i > 0) { columnHeader_source.Width = i; } }
            if (Configuation.GetInt32Arg("OPList.Destination.W", 0, out i)) { if (i > 0) { columnHeader_destination.Width = i; } }
            if (Configuation.GetInt32Arg("OPList.Progress.W", 0, out i)) { if (i > 0) { columnHeader_progress.Width = i; } }

            //Explorer

            if (Configuation.GetBoolArg("Explorer.DoClickSound", 0, out b)) EnablePathChangeSound = b;

            if (Configuation.GetInt32Arg("Explorer.Name.W", 0, out i)) { if (i > 0) { columnHeader_name.Width = i; } }
            if (Configuation.GetInt32Arg("Explorer.Type.W", 0, out i)) { if (i > 0) { columnHeader_typecode.Width = i; } }
            if (Configuation.GetInt32Arg("Explorer.Region.W", 0, out i)) { if (i > 0) { columnHeader_region.Width = i; } }
            if (Configuation.GetInt32Arg("Explorer.Size.W", 0, out i)) { if (i > 0) { columnHeader_size.Width = i; } }

            if (Configuation.GetInt32Arg("Explorer.Viewmode", 0, out i))
            {
                switch (i)
                {
                    default:
                    case 0: //Muda para cover

                        aeroMenuButton_viewmode.Icon = Skin.icon_16x16_view_cover;
                        listView_explorer.ViewMode = ViewMode.Cover;
                        break;

                    case 1: //Muda para list

                        aeroMenuButton_viewmode.Icon = Skin.icon_16x16_view_list;
                        listView_explorer.ViewMode = ViewMode.List;
                        break;

                    case 2: //Muda para details

                        aeroMenuButton_viewmode.Icon = Skin.icon_16x16_view_details;
                        listView_explorer.ViewMode = ViewMode.Details;
                        break;

                    case 3: //Muda para tile

                        aeroMenuButton_viewmode.Icon = Skin.icon_16x16_view_tile;
                        listView_explorer.ViewMode = ViewMode.Tile;
                        break;
                }
            }

            //Atualização

            if (Configuation.GetInt32Arg("Update.Mode", 0, out i)) { if (i >= 0) { ProgramUpdate.UpdateMode = (UpdateMode)i; } }
            if (Configuation.GetBoolArg("Update.NotifyOnly", 0, out b)) { if (i >= 0) { ProgramUpdate.NotifyOnly = b; } }

            if (Configuation.GetInt64Arg("Update.LastUpdate", 0, out l)) { ProgramUpdate.LastUpdate = DateTime.FromBinary(l); }

            //Capas

            if (Configuation.GetBoolArg("Covers.AutoDownload", 0, out b)) { Covers.AutoDownloadCovers = b; }
            if (Configuation.GetBoolArg("Covers.Donwload3dCovers", 0, out b))
            {
                Covers.Download3DCovers = b;
                Covers.Download3DCoversCfg = b;

                if (b)
                {
                    Covers.CoversDir = Covers.CoversDir3D;
                    Covers.ThumbsDir = Covers.ThumbsDir3D;
                }
                else
                {
                    Covers.CoversDir = Covers.CoversDir2D;
                    Covers.ThumbsDir = Covers.ThumbsDir2D;
                }
            }
            if (Configuation.GetBoolArg("Covers.ExportToCompatibleDrive", 0, out b)) { Covers.LookForCompatiblePath = b; }

            if (Configuation.GetStringArg("Covers.Folder", 0, out s)) Covers.CoversDir = Covers.CoversDir.Insert(0, s);
            if (Configuation.GetStringArg("Covers.Thumbs", 0, out s)) Covers.ThumbsDir = Covers.ThumbsDir.Insert(0, s);

            //Operações

            if (Configuation.GetInt32Arg("Operations.Timeout", 0, out i)) { if (i > 0) { OperationManager.OperationTimeout = i; } }
            if (Configuation.GetInt32Arg("Operations.MaxIOPerDrive", 0, out i)) { if (i > 0) { OperationManager.MaximumIOsPerWinDrive = i; } }

            if (Configuation.GetBoolArg("Operations.ExtractScrubbed", 0, out b)) { OperationManager.ExtractDiscScrubbed = b; }
            if (Configuation.GetBoolArg("Operations.AutoUpdateScrubs", 0, out b)) { ScrubDisc.AutoUpdateScrub = b; }
            if (Configuation.GetInt32Arg("Operations.PartitionSelection", 0, out i))
            {
                switch (i)
                {
                    default: //Por padrão só a partição do jogo vai no Scrub
                    case 0: OperationManager.ScrubPartition = PartitionSelection.OnlyGamePartition; break;
                    case 1: OperationManager.ScrubPartition = PartitionSelection.RemoveUpdatePartition; break;
                    case 2: OperationManager.ScrubPartition = PartitionSelection.AllPartitions; break;
                }
            }

            //Log

            if (Configuation.GetBoolArg("Log.SaveLog", 0, out b)) { Log.SaveLog = b; }
            if (Configuation.GetBoolArg("Log.SaveExceptionLog", 0, out b)) { Log.SaveExceptionLog = b; }

            if (Configuation.GetInt32Arg("Operations.MaxIOPerDrive", 0, out i)) { if (i > 0) { Log.MaxLogSize = i; } }

            //Biblioteca

            i = 0;
            while (Configuation.GetStringArg("Library.Folder", i++, out s)) Library.AddFolder(s);

            //Loaders

            i = 0;
            while (Configuation.GetStringArg("Covers.Loader", i++, out s))
            {
                LoaderHomebrew loader;
                if (LoaderHomebrew.TryParse(s, out loader)) Covers.Loaders.Add(loader);
            }

            //Drives excluídos do processo de formatação

            if (Configuation.GetStringArg("NoFormatDrives", 0, out s)) DeviceManager.FormatLockedDrives = s.ToUpper();

            //Drives ignorados

            if (Configuation.GetStringArg("IgnoreDrives", 0, out s)) DeviceManager.IgnoredDrives = s.ToUpper();

            //Fecha janela de formatação

            if (Configuation.GetBoolArg("NoFormatWindow", 0, out b)) DeviceManager.SupressFormatDriveWindow = b;
            if ((Configuation.GetStringArg("FormatWindowCancelText", 0, out s)) && (s != String.Empty))
            {
                DeviceManager.FormatDriveCancelMessage = s;
            }
            else
            {
                DeviceManager.FormatDriveCancelMessage = DeviceManager.GetCancelString();
            }
        }

        //------------------------ Salva as configurações do programa
        private void UpdateConfiguration()
        {
            //Skin

            Configuation.SetStringArg("Skin", 0, Skin.SelectedSkin);

            //Idioma

            Configuation.SetStringArg("Language", 0, Language.SelectedLanguage);

            //Janela Principal

            Configuation.SetInt32Arg("Window.X", 0, Left);
            Configuation.SetInt32Arg("Window.Y", 0, Top);

            Configuation.SetBoolArg("Window.Maximized", 0, (WindowState == FormWindowState.Maximized));

            Configuation.SetInt32Arg("Window.W", 0, Width);
            Configuation.SetInt32Arg("Window.H", 0, Height);

            Configuation.SetInt32Arg("VSplit.X", 0, aeroSplitContainer_vertical.SplitterDistance);
            Configuation.SetInt32Arg("HSplit.Y", 0, aeroSplitContainer_horizontal.SplitterDistance);

            //Lista de operações

            Configuation.SetInt32Arg("OPList.Operation.W", 0, columnHeader_operation.Width);
            Configuation.SetInt32Arg("OPList.Source.W", 0, columnHeader_source.Width);
            Configuation.SetInt32Arg("OPList.Destination.W", 0, columnHeader_destination.Width);
            Configuation.SetInt32Arg("OPList.Progress.W", 0, columnHeader_progress.Width);

            //Explorer

            Configuation.SetBoolArg("Explorer.DoClickSound", 0, EnablePathChangeSound);

            Configuation.SetInt32Arg("Explorer.Name.W", 0, columnHeader_name.Width);
            Configuation.SetInt32Arg("Explorer.Type.W", 0, columnHeader_typecode.Width);
            Configuation.SetInt32Arg("Explorer.Region.W", 0, columnHeader_region.Width);
            Configuation.SetInt32Arg("Explorer.Size.W", 0, columnHeader_size.Width);

            int i = 0;
            switch (listView_explorer.ViewMode)
            {
                default:
                case ViewMode.Cover: i = 0; break;
                case ViewMode.List: i = 1; break;
                case ViewMode.Details: i = 2; break;
                case ViewMode.Tile: i = 3; break;
            }

            Configuation.SetInt32Arg("Explorer.Viewmode", 0, columnHeader_size.Width);

            //Atualização

            Configuation.SetInt32Arg("Update.Mode", 0, (int)ProgramUpdate.UpdateMode);
            Configuation.SetBoolArg("Update.NotifyOnly", 0, ProgramUpdate.NotifyOnly);

            Configuation.SetInt64Arg("Update.LastUpdate", 0, ProgramUpdate.LastUpdate.ToBinary());

            //Capas

            Configuation.SetBoolArg("Covers.AutoDownload", 0, Covers.AutoDownloadCovers);
            Configuation.SetBoolArg("Covers.Donwload3dCovers", 0, Covers.Download3DCoversCfg);
            Configuation.SetBoolArg("Covers.ExportToCompatibleDrive", 0, Covers.LookForCompatiblePath);

            //Operações

            Configuation.SetInt32Arg("Operations.Timeout", 0, OperationManager.OperationTimeout);
            Configuation.SetInt32Arg("Operations.MaxIOPerDrive", 0, OperationManager.MaximumIOsPerWinDrive);

            Configuation.SetBoolArg("Operations.ExtractScrubbed", 0, OperationManager.ExtractDiscScrubbed);
            Configuation.SetBoolArg("Operations.AutoUpdateScrubs", 0, ScrubDisc.AutoUpdateScrub);

            int p = 0;
            switch (OperationManager.ScrubPartition)
            {
                default:
                case PartitionSelection.OnlyGamePartition: p = 0; break;
                case PartitionSelection.RemoveUpdatePartition: p = 1; break;
                case PartitionSelection.AllPartitions: p = 2; break;
            }

            Configuation.SetInt32Arg("Operations.PartitionSelection", 0, p);

            //Log

            Configuation.SetBoolArg("Log.SaveLog", 0, Log.SaveLog);
            Configuation.SetBoolArg("Log.SaveExceptionLog", 0, Log.SaveExceptionLog);

            Configuation.SetInt32Arg("Operations.MaxIOPerDrive", 0, Log.MaxLogSize);

            //Biblioteca

            Configuation.RemoveParameterFromConfiguration("Library.Folder");
            
            for (i = 0; i < Library.Folders.Count; i++)
                Configuation.SetStringArg("Library.Folder", -1, Library.Folders[i].Path);

            //Loaders

            Configuation.RemoveParameterFromConfiguration("Covers.Loader");

            for (i = 0; i < Covers.Loaders.Count; i++)
                Configuation.SetStringArg("Covers.Loader", -1, Covers.Loaders[i].ToString());

            //Drives excluídos do processo de formatação

            Configuation.SetStringArg("NoFormatDrives", 0, DeviceManager.FormatLockedDrives);

            //Drives ignorados pelo programa

            Configuation.SetStringArg("IgnoreDrives", 0, DeviceManager.IgnoredDrives);

            //Fecha a janela de formatação

            Configuation.SetBoolArg("NoFormatWindow", 0, DeviceManager.SupressFormatDriveWindow);
            Configuation.SetStringArg("FormatWindowCancelText", 0, DeviceManager.FormatDriveCancelMessage);

            //
            Configuation.UpdateConfigurationFile("wbfssync.ini");
        }

        //------------------------ Eventos

        //------------------------ Quando o diretório exibido pelo programa muda
        void OnPathChanged()
        {
            pathBox_path.Path = explorerPath;
            quickLinksPanel_quicklinks.SelectLink(explorerPath);
        }

        //------------------------ Quando o conteúdo de uma pasta é modificado
        void OnFolderContentChanged(String folder)
        {
            if (String.Compare(folder, explorerPath, true) == 0) LoadPath(folder);
        }

        //------------------------ Quando uma operação termina
        void OperationManager_OperationEnd(Operation operation)
        {
            if (this.InvokeRequired)
                this.BeginInvoke(new VoidOperationDelegate(OperationManager_OperationEnd_Invoke), operation);
            else
                OperationManager_OperationEnd_Invoke(operation);
        }

        void OperationManager_OperationEnd_Invoke(Operation operation)
        {
            listView_operations.RemoveOperation(operation);

            if (operation.DestinationPath.Length != 0)
            {
                string s = Path.GetDirectoryName(operation.DestinationPath);
                OnFolderContentChanged(s == null ? operation.DestinationPath : s);
            }

            if (operation.SourcePath.Length != 0)
            {
                string s = Path.GetDirectoryName(operation.SourcePath);
                OnFolderContentChanged(s == null ? operation.SourcePath : s);
            }

            OnFolderContentChanged(path_computer);

            tab_operations.Caption = String.Format("{0} ({1}/{2})", Language.tab_operations,
                OperationManager.ActiveOperations, OperationManager.TotalOperations);
        }

        //------------------------ Quando uma operação começa
        void OperationManager_OperationStart(Operation operation)
        {
            if (this.InvokeRequired)
                this.BeginInvoke(new VoidOperationDelegate(OperationManager_OperationStart_Invoke), operation);
            else
                OperationManager_OperationStart_Invoke(operation);
        }

        void OperationManager_OperationStart_Invoke(Operation operation)
        {
            listView_operations.AddOperation(operation);
            OnFolderContentChanged(path_computer);
            tab_operations.Caption = String.Format("{0} ({1}/{2})", Language.tab_operations,
                OperationManager.ActiveOperations, OperationManager.TotalOperations);
        }

        //------------------------ Quando uma operação é ativada
        void OperationManager_OperationActivated(Operation operation)
        {
            if (this.InvokeRequired)
                this.BeginInvoke(new VoidOperationDelegate(OperationManager_OperationActivated_Invoke), operation);
            else
                OperationManager_OperationActivated_Invoke(operation);
        }

        void OperationManager_OperationActivated_Invoke(Operation operation)
        {
            tab_operations.Caption = String.Format("{0} ({1}/{2})", Language.tab_operations,
                OperationManager.ActiveOperations, OperationManager.TotalOperations);
        }

        //------------------------ Quando a lista de cópia/recorte/cola muda
        void OperationManager_ClipboardChanged(string[] clipboard)
        {
            listView_clipboard.UpdateClipboardList(clipboard);
            tab_clipboard.Caption = Language.tab_clipboard + " (" + clipboard.Length + ')';

            SetPasteState((explorerPath != path_computer) && (explorerPath != path_covers) &&
                ((explorerPath != path_library) || (Library.Folders.Count > 0)));
        }

        //------------------------ Quando a lista de dispositivos ativos muda
        void DeviceManager_DeviceListChanged(bool devicesInserted, bool devicesRemoved, bool devicesUpdated)
        {
            if (!SuspendDeviceAndLibraryEvents)
            {
                Log.LogLine(Language.log_device_update, LogMessageType.Message);
                Log.LogLine(Language.log_devices_found, LogMessageType.Message, DeviceManager.DriveCount,
                    DeviceManager.WBFSDriveCount);

                Log.LogLine(Language.log_synccovers, LogMessageType.Message);

                SetupQuickLinks();
                SetupSendToMenuContext();

                if (devicesInserted || devicesUpdated)
                {
                    int newcovers = Covers.SynchronizeFromDevices(true);
                    if (newcovers > 0) 
                        Log.LogLine(Language.log_new_covers_found, LogMessageType.Message, newcovers);
                }
                if (devicesRemoved)
                {
                    if (explorerPath[0] != '#') //Não é uma pasta especial
                    {
                        if (DeviceManager.GetDrive(Path.GetPathRoot(explorerPath)) == null)
                        {
                            LoadComputer();
                            return;
                        }
                    }
                }

                OnFolderContentChanged(path_computer);
            }
        }

        //------------------------ Quando a libraria de jogos muda
        void Libraries_LibraryChanged()
        {
            if (this.InvokeRequired) //Garante que eventos disparados por threads diferentes não gerem exceção
                this.BeginInvoke(new VoidVoidDelegate(Libraries_LibraryChanged_Invoke));
            else
                Libraries_LibraryChanged_Invoke();
        }

        void Libraries_LibraryChanged_Invoke()
        {
            if (!SuspendDeviceAndLibraryEvents)
            {
                Covers.SynchronizeFromLibraries(true);
                SetupQuickLinks();
                SetupSendToMenuContext();
                OnFolderContentChanged(path_library);
            }
        }

        //------------------------ Quando novas capas são achadas
        void Covers_CoverListChanged(object sender, EventArgs e)
        {
            if (this.InvokeRequired)
                this.BeginInvoke(new VoidStringDelegate(LoadPath), explorerPath);
            else
                LoadPath(explorerPath);
        }

        //------------------------ Atualiza a exibição do Log
        void Log_AppendLogLine(string line, LogMessageType messagetype)
        {
            if (this.InvokeRequired)
                this.BeginInvoke(new VoidStringLogMessageTypeDelegate(Log_AppendLogLine_Invoke), line, messagetype);
            else
                Log_AppendLogLine_Invoke(line, messagetype);
        }

        void Log_AppendLogLine_Invoke(string line, LogMessageType messagetype)
        {
            LogLineCount++;
            tab_log.Caption = Language.tab_log + " (" + LogLineCount + ')';
            listView_log.AddLine(line, messagetype);
        }

        //------------------------ Quando um pedido de formatação de texto para um log é requisitado
        //por um módulo
        void Log_ObjectMessageSent(string sender, int code, object extendedinfo, LogMessageType type)
        {
            Log.LogLine(Language.FormatDllCode(sender, code, extendedinfo), type);
        }

        //------------------------ Atualiza a exibição do Log
        void Log_LastLineChanged(string part)
        {
            if (this.InvokeRequired)
                this.BeginInvoke(new VoidStringDelegate(listView_log.UpdateLastLine), part);
            else
                listView_log.UpdateLastLine(part);
        }

        //------------------------ Quando é chamada uma operação especial do atalho
        void quickLinksPanel_quicklinks_QuickLinkSpecialClick(QuickLink link)
        {
            TagMetadata metadata = (TagMetadata)link.Tag;
            switch (metadata.Type)
            {
                case SelectionType.Drive: //Ripar DVD para a pasta atual

                    OperationManager.RipDvd(((TagMetadata)link.Tag).Path, explorerPath);

                    break;

                case SelectionType.Covers: //Exportar Capas

                    contextMenuStrip_export_covers.Show(MousePosition);

                    break;

                case SelectionType.Folder: //Remover da Biblioteca

                    if (MessageBox.Show(this, Language.message_removefromlibrary, Language.AppName, MessageBoxButtons.YesNo,
                        MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) == DialogResult.Yes)
                    {
                        Library.RemoveFolder(metadata.Path, false);
                    }

                    break;

                case SelectionType.Library: //Adicionar à Biblioteca

                    if (ShowFolderBrowseDialog(Language.field_dialog_name_add_to_library, Environment.SpecialFolder.Desktop) == 
                        DialogResult.OK)
                    {
                        Library.AddFolder(folderBrowserDialog_main.SelectedPath);
                    }
                    break;
            }
        }

        //------------------------ Quando um atalho é clicado
        void quickLinksPanel_quicklinks_QuickLinkClick(QuickLink link)
        {
            String path = ((TagMetadata)link.Tag).Path;
            if (path != String.Empty)
            {
                CloseConfigurationPanel();
                LoadPath(path);
            }
        }

        //------------------------ Limpa a biblioteca
        private void toolStripMenuItem_organize_lib_clear_Click(object sender, EventArgs e)
        {
            Library.Clear();
        }

        //------------------------ Muda para a guia de Operações em andamento/pendentes
        private void tab_operations_Click(object sender, EventArgs e)
        {
            tab_operations.Selected = true;
            tab_log.Selected = false;
            tab_clipboard.Selected = false;

            listView_operations.Visible = true;
            listView_log.Visible = false;
            listView_clipboard.Visible = false;
        }

        //------------------------ Muda para a guia do Log
        private void tab_log_Click(object sender, EventArgs e)
        {
            tab_operations.Selected = false;
            tab_log.Selected = true;
            tab_clipboard.Selected = false;

            listView_operations.Visible = false;
            listView_log.Visible = true;
            listView_clipboard.Visible = false;

            listView_log.EnsureVisible(listView_log.Items.Count - 1);
        }

        //------------------------ Muda para a guia de Clipboard
        private void tab_clipboard_Click(object sender, EventArgs e)
        {
            tab_operations.Selected = false;
            tab_log.Selected = false;
            tab_clipboard.Selected = true;

            listView_operations.Visible = false;
            listView_log.Visible = false;
            listView_clipboard.Visible = true;
        }

        //------------------------ Quando um item sofre um clique duplo do mouse
        private void listView_explorer_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            if (listView_explorer.SelectedItems.Count != 1) return;

            TagMetadata metadata = (TagMetadata)listView_explorer.SelectedItems[0].Tag;
            switch (metadata.Type)
            {
                case SelectionType.Folder:
                    LoadFolder(metadata.Path);
                    break;

                case SelectionType.Drive:
                    IDrive drive = (IDrive)metadata.Meta1;
                    if (drive.Is(DriveFlags.WBFS))
                    {
                        LoadWBFSDrive((WBFSDrive)drive);
                    }
                    else
                    {
                        LoadWinDrive(drive);
                    }
                    break;
            }
        }

        //------------------------ Enter simula 2 cliques
        private void listView_explorer_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar == (char)Keys.Return) listView_explorer_MouseDoubleClick(sender, null);
        }

        //------------------------ Quando itens são selecionados/desselecionados
        private void listView_explorer_SelectedIndexChanged(object sender, EventArgs e)
        {
            if(SelectedItemsCount == 0)
            { 
                SelectedType = SelectionType.Invalid;

                BeginEditingMenu();

                SetFormatState(false);
                SetDeleteState(false);
                SetCutCopyRenameStates(false);
                SetCompressDecompressStates(false, false);

                FinishEditingMenu();

                LastSelectedItemsCount = SelectedItemsCount;
                return;
            }

            SelectionType LastSelectedType = SelectedType;
            SelectedType = (SelectionType)0xFF;

            foreach (ListViewItem item in listView_explorer.SelectedItems)
            {
                TagMetadata meta = (TagMetadata)item.Tag;
                SelectedType &= meta.Type;
            }

            if ((SelectedType == LastSelectedType) && (SelectedItemsCount == LastSelectedItemsCount))
            {
                LastSelectedItemsCount = SelectedItemsCount;
                return;
            }

            BeginEditingMenu();

            switch (SelectedType)
            {
                case SelectionType.Folder: //Só permite excluir, e apenas da biblioteca

                    SetFormatState(false);
                    SetDeleteState(explorerPath == path_library);
                    SetCutCopyRenameStates(false);
                    SetCompressDecompressStates(false, false);
                    break;

                case SelectionType.Covers: //Só permite excluir as capas

                    SetFormatState(false);
                    SetDeleteState(true);
                    SetCutCopyRenameStates(false);
                    SetCompressDecompressStates(false, false);
                    break;

                case SelectionType.Drive:

                    SetDeleteState(false);
                    SetCutCopyRenameStates(false);
                    SetCompressDecompressStates(false, false);

                    if (SelectedItemsCount == 1)
                    {
                        IDrive drive = (IDrive)((TagMetadata)listView_explorer.SelectedItems[0].Tag).Meta1;
                        SetFormatState(drive.Ready && !drive.Is(DriveFlags.WinDrive) &&
                            !DeviceManager.FormatLockedDrives.Contains(Char.ToUpper(drive.Name[0])) &&
                            (drive.Is(DriveFlags.Removable) || drive.Is(DriveFlags.Fixed)));
                    }
                    else
                    {
                        SetFormatState(false);
                    }

                    break;

                //case SelectionType.Rar:
                //    break;

                case SelectionType.Disc:

                    SetFormatState(false);
                    SetDeleteState(true);
                    SetCutCopyRenameStates(true);

                    byte op = 0xFF;
                    foreach (ListViewItem item in listView_explorer.SelectedItems)
                        op &= (byte)((TagMetadata)listView_explorer.SelectedItems[0].Tag).Meta2;

                    SetCompressDecompressStates(op == 0x1, op == 0x2);

                    break;

                default:
                    SetFormatState(false);
                    SetDeleteState(false);
                    SetCutCopyRenameStates(false);
                    SetCompressDecompressStates(false, false);
                    break;
            }

            FinishEditingMenu();

            LastSelectedItemsCount = SelectedItemsCount;
        }

        //------------------------ Altera a posição do indicador de pasta vazia
        void listView_explorer_ViewModeChanged(object sender, EventArgs e)
        {
            if (listView_explorer.ViewMode == ViewMode.Details)
            {
                label_empty_folder.Top = 43;
            }
            else
            {
                label_empty_folder.Top = 18;
            }
        }

        //------------------------ O usuário força a atualização de um diretório
        void pathBox_path_RefreshClick(object sender, string path)
        {
            CloseConfigurationPanel();
            LoadPath(path);
        }

        //------------------------ Quando o usuário altera o diretório pela caixa de caminho
        void pathBox_path_PathClick(object sender, string path)
        {
            CloseConfigurationPanel();
            LoadPath(path);
        }

        //------------------------ Exporta capas para um determinado Loader
        void LoaderHomebrew_Click(object sender, EventArgs e)
        {
            int loader = (int)(sender as ToolStripItem).Tag;

            if (loader >= 0)
            {
                String basepath = Covers.Loaders[loader].Path;

                if (Covers.LookForCompatiblePath)
                {
                    IDrive drive = null;
                    int i = 0;
                    while ((drive = DeviceManager.GetDrive(i++)) != null)
                    {
                        if ((!drive.Ready) || (drive.Is(DriveFlags.WBFS))) continue;
                        if (Directory.Exists(drive.Name + basepath))
                        {
                            OperationManager.ExportCovers(loader, drive.Name);
                            return;
                        }
                    }
                }
            }

            //Não achou diretório compatível, Covers.LookForCompatiblePath = false, ou foi clicado "enviar para..."
            if (ShowFolderBrowseDialog(Language.message_browsedrive, Environment.SpecialFolder.MyComputer) ==
                DialogResult.OK)
            {
                OperationManager.ExportCovers(loader, folderBrowserDialog_main.SelectedPath + '\\');
            }
        }

        //------------------------ Sincroniza os cóigos existentes com o gerenciador e baixa as capas novas
        void SynchronizeCover_Click(object sender, EventArgs e)
        {
            Covers.SynchronizeFromDevices(true);
        }

        //------------------------ Alterna entre as exibições
        private void aeroMenuButton_viewmode_Click(object sender, EventArgs e)
        {
            //Não permite troca de viewmode se estiver no modo capa
            if (listView_explorer.ForceFullSize) return;

            switch (listView_explorer.ViewMode)
            {
                case ViewMode.Tile: //Muda para cover

                    aeroMenuButton_viewmode.Icon = Skin.icon_16x16_view_cover;
                    listView_explorer.ViewMode = ViewMode.Cover;
                    break;

                case ViewMode.Cover: //Muda para list

                    aeroMenuButton_viewmode.Icon = Skin.icon_16x16_view_list;
                    listView_explorer.ViewMode = ViewMode.List;
                    break;

                case ViewMode.List: //Muda para details

                    aeroMenuButton_viewmode.Icon = Skin.icon_16x16_view_details;
                    listView_explorer.ViewMode = ViewMode.Details;
                    break;

                case ViewMode.Details: //Muda para tile

                    aeroMenuButton_viewmode.Icon = Skin.icon_16x16_view_tile;
                    listView_explorer.ViewMode = ViewMode.Tile;
                    break;
            }
        }

        //------------------------ Muda a exibição para capas
        private void toolStripMenuItem_viewmode_cover_Click(object sender, EventArgs e)
        {
            //Não permite troca de viewmode se estiver no modo capa
            if (listView_explorer.ForceFullSize) return;

            aeroMenuButton_viewmode.Icon = Skin.icon_16x16_view_cover;
            listView_explorer.ViewMode = ViewMode.Cover;
        }

        //------------------------ Muda a exibição para lado a lado
        private void toolStripMenuItem_viewmode_tile_Click(object sender, EventArgs e)
        {
            //Não permite troca de viewmode se estiver no modo capa
            if (listView_explorer.ForceFullSize) return;

            aeroMenuButton_viewmode.Icon = Skin.icon_16x16_view_tile;
            listView_explorer.ViewMode = ViewMode.Tile;
        }

        //------------------------ Muda a exibição para lista
        private void toolStripMenuItem_viewmode_list_Click(object sender, EventArgs e)
        {
            //Não permite troca de viewmode se estiver no modo capa
            if (listView_explorer.ForceFullSize) return;

            aeroMenuButton_viewmode.Icon = Skin.icon_16x16_view_list;
            listView_explorer.ViewMode = ViewMode.List;
        }

        //------------------------ Muda a exibição para detalhes
        private void toolStripMenuItem_viewmode_details_Click(object sender, EventArgs e)
        {
            //Não permite troca de viewmode se estiver no modo capa
            if (listView_explorer.ForceFullSize) return;

            aeroMenuButton_viewmode.Icon = Skin.icon_16x16_view_details;
            listView_explorer.ViewMode = ViewMode.Details;
        }

        //------------------------ Operações de arquivo / pasta

        //------------------------ Converte os itens selecionados para String[] contendo seus caminhos
        String[] GetSeletectItemsPaths()
        {
            String[] s = new String[SelectedItemsCount];
            for (int i = 0; i < s.Length; i++) 
                s[i] = ((TagMetadata)listView_explorer.SelectedItems[i].Tag).Path;

            return s;
        }

        //------------------------ Copia os itens selecionados para o clipboard do programa
        void Copy(object sender, EventArgs e)
        {
            String[] files = GetSeletectItemsPaths();
            OperationManager.Copy(files);
        }

        //------------------------ Recorta os itens selecionados para o clipboard do programa
        void Cut(object sender, EventArgs e)
        {
            String[] files = GetSeletectItemsPaths();
            OperationManager.Cut(files);
        }

        //------------------------ Cola os itens selecionados do clipboard do programa
        void Paste(object sender, EventArgs e)
        {
            OperationManager.Paste(explorerPath);
        }

        void Compress(object sender, EventArgs e)
        {
            if (ShowFolderBrowseDialog("", explorerPath) == DialogResult.OK)
            {
                if (folderBrowserDialog_main.SelectedPath[folderBrowserDialog_main.SelectedPath.Length - 1] != '\\')
                {
                    folderBrowserDialog_main.SelectedPath += '\\';
                }

                String[] files = GetSeletectItemsPaths();
                OperationManager.Compress(files, folderBrowserDialog_main.SelectedPath);
            }
        }

        void Decompress(object sender, EventArgs e)
        {
            if (ShowFolderBrowseDialog("", explorerPath) == DialogResult.OK)
            {
                if (folderBrowserDialog_main.SelectedPath[folderBrowserDialog_main.SelectedPath.Length - 1] != '\\')
                {
                    folderBrowserDialog_main.SelectedPath += '\\';
                }

                String[] files = GetSeletectItemsPaths();
                OperationManager.Decompress(files, folderBrowserDialog_main.SelectedPath);
            }
        }

        void Delete(object sender, EventArgs e)
        {
            String[] files = GetSeletectItemsPaths();

            switch (explorerPath)
            {
                case path_covers:

                    if (MessageBox.Show(Language.message_delete_covers, Language.AppName, MessageBoxButtons.YesNo,
                        MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) == DialogResult.Yes)
                    {
                        Covers.DeleteCovers(files);
                        OnFolderContentChanged(path_covers);
                    }

                    break;

                case path_library:

                    if (((TagMetadata)listView_explorer.SelectedItems[0].Tag).Type == SelectionType.Folder)
                    {
                        if (MessageBox.Show(Language.message_delete_lib_folders, Language.AppName, MessageBoxButtons.YesNo,
                            MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) == DialogResult.Yes)
                        {
                            Library.RemoveFolders(files);
                        }
                    }
                    else
                    {
                        OperationManager.Delete(files);
                        OnFolderContentChanged(path_library);
                    }

                    break;

                default:
                    OperationManager.Delete(files);
                    OnFolderContentChanged(explorerPath);
                    break;
            }
        }

        void Rename(object sender, EventArgs e)
        {
            ListViewItem item = listView_explorer.SelectedItems[0];

            FormRenameDisc rename = new FormRenameDisc();
            rename.SetupLanguage();
            rename.textBox_name.Text = item.SubItems[0].Text;
            rename.textBox_code.Text = item.SubItems[1].Text;
            if (rename.ShowDialog() == DialogResult.OK)
            {
                String file = ((TagMetadata)item.Tag).Path;
                OperationManager.Rename(file, rename.NewName, rename.NewCode);
                OnFolderContentChanged(explorerPath);
            }
        }

        void SendTo(object sender, EventArgs e)
        {
            String path = (String)(sender as ToolStripMenuItem).Tag;

            String[] files = GetSeletectItemsPaths();
            OperationManager.MoveFiles(files, path, false);
        }

        void Format(object sender, EventArgs e)
        {
            IDrive drive = (IDrive)((TagMetadata)listView_explorer.SelectedItems[0].Tag).Meta1;

            if (!drive.Ready)
            {
                MessageBox.Show(Language.message_drive_not_ready, Language.AppName, MessageBoxButtons.OK);
                return;
            }

            if (drive.ActiveOperations > 0)
            {
                MessageBox.Show(Language.message_drive_in_use, Language.AppName, MessageBoxButtons.OK);
                return;
            }

            if (MessageBox.Show(Language.message_format_drive, Language.AppName, MessageBoxButtons.YesNo,
                MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) ==
                        DialogResult.Yes)
            {
                if (drive.Is(DriveFlags.WBFS))
                {
                    WBFSDevice device = ((WBFSDrive)drive)._Device;
                    device.Format();
                    drive.Refresh();
                }
                else
                {
                    if (WBFSDevice.Format(drive.Name) == 0)//Formatação bem sucedida, atualiza a estrutura IDrive
                    {
                        DeviceManager.EnqueueDriveRemotion(drive.Name);
                        DeviceManager.EnqueueDriveInsertions(drive.Name);
                        DeviceManager.ProcessPendingDrives(false);
                    }
                }

                if (explorerPath.StartsWith(drive.Name)) LoadPath(drive.Name);
                if (explorerPath.StartsWith(path_computer)) LoadPath(path_computer);

                drive = null;
            }
        }

        //------------------------ Validação

        //------------------------ Verifica se uma pasta pode ser carregada
        bool ValidadeFolder(String folder)
        {
            try
            {
                Directory.GetDirectories(folder);
                return true;
            }
            catch (Exception e)
            {
                Log.LogException(e);

                MessageBox.Show(this, Language.message_access_denied, Language.AppName, MessageBoxButtons.OK,
                     MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);

                return false;
            }
        }

        //------------------------ Verifica se um drive pode ser carregado
        bool ValidadeDrive(IDrive drive)
        {
            if (drive == null)
            {
                MessageBox.Show(this, Language.message_invalid_drive, Language.AppName, MessageBoxButtons.OK,
                     MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
                return false;
            }

            if ((!drive.Ready) || (drive.Size <= 0))
            {
                MessageBox.Show(this, Language.message_drive_not_ready, Language.AppName, MessageBoxButtons.OK,
                     MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
                return false;
            }

            return true;
        }

        //------------------------ GUI

        //------------------------ Mostra a janela de seleção de diretório
        DialogResult ShowFolderBrowseDialog(string text, Environment.SpecialFolder rootfolder)
        {
            folderBrowserDialog_main.Description = text;
            folderBrowserDialog_main.RootFolder = rootfolder;
            return folderBrowserDialog_main.ShowDialog(this);
        }

        DialogResult ShowFolderBrowseDialog(string text, String path)
        {
            folderBrowserDialog_main.Description = text;
            folderBrowserDialog_main.RootFolder = Environment.SpecialFolder.Desktop;
            folderBrowserDialog_main.SelectedPath = path;
            return folderBrowserDialog_main.ShowDialog(this);
        }

        //------------------------ Mostra a janela para abrir arquivos
        DialogResult ShowOpenFileDialog(String title, String filter, Boolean multiselect)
        {
            openFileDialog_main.Title = title;
            openFileDialog_main.Filter = filter;
            openFileDialog_main.Multiselect = multiselect;
            return openFileDialog_main.ShowDialog(this);
        }

        //------------------------ Mostra a janela para salvar arquivo
        DialogResult ShowSaveFileDialog(String title, String filter)
        {
            saveFileDialog_main.Filter = filter;
            saveFileDialog_main.Title = title;
            return saveFileDialog_main.ShowDialog(this);
        }

        //------------------------ Fecha o painel de configuração se estiver aberto
        void CloseConfigurationPanel()
        {
            if (configurationPanel_configuration.Visible)
            {
                if (MessageBox.Show(this, Language.message_apply_config, Language.AppName,
                    MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) == DialogResult.Yes)
                {
                    configurationPanel_configuration.Apply();
                }
            }
        }

        //------------------------ Prepara o Menu para edição
        void BeginEditingMenu()
        {
            panel_menu.SuspendLayout();
        }

        //------------------------ Termina a edição do Menu
        void FinishEditingMenu()
        {
            panel_menu.ResumeLayout();
            panel_menu.OrganizeControls();
        }

        //------------------------ Seta o status de alguns componentes quando se torna possível compactar ou descompactar
        void SetCompressDecompressStates(bool cstate, bool dstate)
        {
            toolStripMenuItem_explorer_compress.Enabled = cstate;
            toolStripMenuItem_organize_compress.Enabled = cstate;
            aeroMenuButton_compress.Visible = cstate;

            toolStripMenuItem_explorer_decompress.Enabled = dstate;
            toolStripMenuItem_organize_decompress.Enabled = dstate;
            aeroMenuButton_decompress.Visible = dstate;
        }

        //------------------------ Seta o status de alguns componentes quando se torna possível copiar/recortar/renomear
        void SetCutCopyRenameStates(bool state)
        {
            toolStripMenuItem_organize_copy.Enabled = state;
            toolStripMenuItem_organize_cut.Enabled = state;
            toolStripMenuItem_organize_rename.Enabled = state && (SelectedItemsCount == 1);

            toolStripMenuItem_explorer_downloadcover.Enabled = state;
            toolStripMenuItem_explorer_copy.Enabled = state;
            toolStripMenuItem_explorer_cut.Enabled = state;
            toolStripMenuItem_explorer_rename.Enabled = state && (SelectedItemsCount == 1);

            toolStripMenuItem_explorer_sendto.Enabled = state; //Se enquadra como "Copiar"

            aeroMenuButton_copy.Visible = state;
            aeroMenuButton_cut.Visible = state;
            aeroMenuButton_rename.Visible = state && (SelectedItemsCount == 1);

            pictureBox_menu_separator_4.Visible = (aeroMenuButton_copy.Visible || aeroMenuButton_cut.Visible || 
                aeroMenuButton_paste.Visible);
            pictureBox_menu_separator_3.Visible = (aeroMenuButton_delete.Visible || aeroMenuButton_rename.Visible);
        }

        //------------------------ Seta o status de alguns componentes quando se torna possível apagar
        void SetDeleteState(bool state)
        {
            toolStripMenuItem_organize_delete.Enabled = state;
            toolStripMenuItem_explorer_delete.Enabled = state;
            aeroMenuButton_delete.Visible = state;

            pictureBox_menu_separator_3.Visible = (aeroMenuButton_delete.Visible || aeroMenuButton_rename.Visible);
        }

        //------------------------ Seta o status de alguns componentes quando se torna possível formatar um drive
        void SetFormatState(bool state)
        {
            aeroMenuButton_format.Visible = state;
        }

        //------------------------ Seta o status de alguns componentes quando se torna possível colar
        void SetPasteState(bool state)
        {
            toolStripMenuItem_organize_paste.Enabled = state && OperationManager.CanPaste;
            toolStripMenuItem_explorer_paste.Enabled = state && OperationManager.CanPaste;
            aeroMenuButton_paste.Visible = state && OperationManager.CanPaste;

            pictureBox_menu_separator_4.Visible = (aeroMenuButton_copy.Visible || aeroMenuButton_cut.Visible ||
                aeroMenuButton_paste.Visible);
        }

        //------------------------ Carrega o Menu de contexto de capas
        void SetupCoversMenuContext()
        {
            contextMenuStrip_export_covers.SuspendLayout();
            contextMenuStrip_export_covers.Items.Clear();

            contextMenuStrip_export_covers.Items.Add(Language.menuitem_synchronizecover, null, 
                new EventHandler(SynchronizeCover_Click));

            if (Covers.Loaders.Count > 0)
            {
                contextMenuStrip_export_covers.Items.Add("-");

                for (int i = 0; i < Covers.Loaders.Count; i++)
                {
                    contextMenuStrip_export_covers.Items.Add(String.Format(Language.menuitem_exportcoversto,
                        Covers.Loaders[i].Name), null, new EventHandler(LoaderHomebrew_Click));
                    contextMenuStrip_export_covers.Items[2 + i].Tag = i;
                }
            }

            contextMenuStrip_export_covers.Items.Add("-");

            contextMenuStrip_export_covers.Items.Add(Language.menuitem_sendto + "...", null, 
                new EventHandler(LoaderHomebrew_Click));
            contextMenuStrip_export_covers.Items[contextMenuStrip_export_covers.Items.Count - 1].Tag = -1;

            contextMenuStrip_export_covers.ResumeLayout();
        }

        //------------------------ Carrega o Menu de contexto de Enviar Para
        void SetupSendToMenuContext()
        {
            contextMenuStrip_sendTo.SuspendLayout();
            contextMenuStrip_sendTo.Items.Clear();

            ToolStripMenuItem item = new ToolStripMenuItem(Language.foldername_desktop, Skin.icon_16x16_desktop,
                new EventHandler(SendTo));
            item.Tag = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
            contextMenuStrip_sendTo.Items.Add(item);

            item = new ToolStripMenuItem(Language.foldername_documents, Skin.icon_16x16_documents,
                new EventHandler(SendTo));
            item.Tag = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
            contextMenuStrip_sendTo.Items.Add(item);

            contextMenuStrip_sendTo.Items.Add("-");

            if (Library.Folders.Count > 0)
            {
                item = new ToolStripMenuItem(Language.foldername_library + " (" +
                Path.GetFileName(Library.Folders[0].Path) + ')', Skin.icon_16x16_library,
                    new EventHandler(SendTo));
                item.Tag = Library.Folders[0].Path;
                contextMenuStrip_sendTo.Items.Add(item);

                contextMenuStrip_sendTo.Items.Add("-");
            }

            int i;
            IDrive drive;

            if (DeviceManager.WBFSDriveCount > 0)
            {
                i = 0;
                while ((drive = DeviceManager.GetWBFSDrive(i++)) != null)
                {
                    if (!drive.Ready) continue;

                    item = new ToolStripMenuItem(FormatDriveName(drive), Skin.icon_16x16_wbfsdrive,
                        new EventHandler(SendTo));
                    item.Tag = drive.Name;

                    contextMenuStrip_sendTo.Items.Add(item);
                }

                contextMenuStrip_sendTo.Items.Add("-");
            }

            i = 0;
            while ((drive = DeviceManager.GetWindowsDrive(i++)) != null)
            {
                if (!drive.Ready) continue;

                item = new ToolStripMenuItem(FormatDriveName(drive), ExplorerListView.GetSmallDriveIcon(drive),
                    new EventHandler(SendTo));
                item.Tag = drive.Name;

                contextMenuStrip_sendTo.Items.Add(item);
            }

            contextMenuStrip_sendTo.ResumeLayout();
        }

        //------------------------ Carrega os QuickLinks
        void SetupQuickLinks()
        {
            quickLinksPanel_quicklinks.SuspendLayout();
            quickLinksPanel_quicklinks.Controls.Clear();

            int y = 9;
            int i = 0;
            //----- Favoritos
            
            quickLinksPanel_quicklinks.AddQuickLink(y, Language.foldername_favorites,
                new TagMetadata("", SelectionType.Invalid, null), Skin.icon_16x16_favorites, 0);
            y += 21;
            quickLinksPanel_quicklinks.AddQuickLink(y, Language.foldername_desktop, new TagMetadata(
                Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), SelectionType.Folder, null),
                Skin.icon_16x16_desktop, 1);
            y += 21;
            quickLinksPanel_quicklinks.AddQuickLink(y, Language.foldername_documents, new TagMetadata(
                Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), SelectionType.Folder, null),
                Skin.icon_16x16_documents, 1);
            y += 21;
            quickLinksPanel_quicklinks.AddQuickLink(y, Language.foldername_covers, Language.tooltip_exportcover, 
                new TagMetadata(path_covers, SelectionType.Covers, null), Skin.icon_16x16_covers, 
                Skin.icon_11x11_export_covers, Skin.icon_11x11_export_covers_o, 1);
            y += 21;

            //----- Bibliotecas
            y += 21;

            quickLinksPanel_quicklinks.AddQuickLink(y, Language.foldername_library, Language.tooltip_addlibrary,
                new TagMetadata(path_library, SelectionType.Library, null), Skin.icon_16x16_library,
                Skin.icon_11x11_add, Skin.icon_11x11_add_o, 0);
            y += 21;

            for (i = 0; i < Library.Folders.Count; i++)
            {
                String text = Path.GetFileName(Library.Folders[i].Path);
                quickLinksPanel_quicklinks.AddQuickLink(y, (text.Length == 0 ? Library.Folders[i].Path : text), 
                    Language.tooltip_removelibrary, new TagMetadata(Library.Folders[i].Path, SelectionType.Folder, null), 
                    Skin.icon_16x16_folder, Skin.icon_11x11_remove, Skin.icon_11x11_remove_o, 1);
            
                y += 21;
            }

            //----- Computador
            y += 21;

            quickLinksPanel_quicklinks.AddQuickLink(y, Language.foldername_computer, new TagMetadata(path_computer,
                SelectionType.Folder, null), Skin.icon_16x16_computer, 0);
            y += 21;

            IDrive drive = null;

            i = 0;
            while ((drive = DeviceManager.GetWBFSDrive(i++)) != null)
            {
                quickLinksPanel_quicklinks.AddQuickLink(y, FormatDriveName(drive), new TagMetadata(drive.Name,
                    SelectionType.Drive, null), Skin.icon_16x16_wbfsdrive, 1);
                
                y += 21;
            }

            i = 0;
            while ((drive = DeviceManager.GetWindowsDrive(i++)) != null)
            {
                if (!drive.Ready) continue;

                else if(drive.Is(DriveFlags.Removable))
                {
                    quickLinksPanel_quicklinks.AddQuickLink(y, FormatDriveName(drive), new TagMetadata(drive.Name,
                    SelectionType.Drive, null), Skin.icon_16x16_usbdrive, 1);
                }
                else if (drive.Is(DriveFlags.WinDrive))
                {
                    quickLinksPanel_quicklinks.AddQuickLink(y, FormatDriveName(drive), new TagMetadata(drive.Name,
                    SelectionType.Drive, null), Skin.icon_16x16_windrive, 1);
                }
                else if (drive.Is(DriveFlags.Fixed))
                {
                    quickLinksPanel_quicklinks.AddQuickLink(y, FormatDriveName(drive), new TagMetadata(drive.Name,
                    SelectionType.Drive, null), Skin.icon_16x16_harddrive, 1);
                }
                else if (drive.Is(DriveFlags.CDRom))
                {
                    CDDrive cddrive = (CDDrive)drive;

                    if (cddrive._WiiDiscInserted == true)
                    {
                        quickLinksPanel_quicklinks.AddQuickLink(y, FormatDriveName(drive), Language.tooltip_ripdvd,
                            new TagMetadata(drive.Name, SelectionType.Drive, null),
                            Skin.icon_16x16_cddrive_full, Skin.icon_11x11_ripdvd, Skin.icon_11x11_ripdvd_o, 1);
                    }
                    else
                    {
                        quickLinksPanel_quicklinks.AddQuickLink(y, FormatDriveName(drive),
                            new TagMetadata(drive.Name, SelectionType.Drive, null), Skin.icon_16x16_cddrive_full, 1);
                    }
                }

                y += 21;
            }

            quickLinksPanel_quicklinks.ResumeLayout();
        }

        //------------------------ Avalia e carrega um caminho qualquer
        void LoadPath(String path)
        {
            if (path.Length == 0) return;
            else if (path[0] == '#') //Pasta especial
            {
                if (path == path_computer)
                {
                    LoadComputer();
                }
                else if (path == path_library)
                {
                    LoadLibrary();
                }
                else if (path == path_covers)
                {
                    LoadCovers();
                }
            }
            else if (path.Length > 3) //Pasta
            {
                LoadFolder(path);
            }
            else //Drive, valida e verifica se é WBFS
            {
                IDrive drive = DeviceManager.GetDrive(path);
                if (drive.Is(DriveFlags.WBFS))
                {
                    LoadWBFSDrive((WBFSDrive)drive);
                }
                else
                {
                    LoadWinDrive(drive);
                }
            }
        }

        //------------------------ Carrega a Pasta Computador
        void LoadComputer()
        {
            ExplorerPath = path_computer;
            listView_explorer.SuspendLayout();
            listView_explorer.SuspendCoverMode = true;
            listView_explorer.ForceFullSize = false;

            foreach (ListViewItem k in listView_explorer.SelectedItems) k.Selected = false;

            listView_explorer.Items.Clear();
            label_empty_folder.Visible = false;

            SetPasteState(false); //Nada de colar coisas em pastas especiais...

            if(EnablePathChangeSound)
                PathChangeSound.Play();

            //
            IDrive drive = null;
            for (int i = 0; i < DeviceManager.DriveCount; i++)
            {
                drive = DeviceManager.GetDrive(i);
                if (drive != null)
                {
                    ListViewItem item = new ListViewItem();
                    item.Name = drive.Name;
                    item.Text = FormatDriveName(drive);
                    item.ToolTipText = item.Name;
                    item.Tag = new TagMetadata("", SelectionType.Drive, drive);

                    if (drive.Is(DriveFlags.WBFS))
                    {
                        item.Group = WBFSGroup;
                        item.SubItems.Add(Language.drivename_wbfs);
                    }
                    else if (drive.Is(DriveFlags.Removable))
                    {
                        item.Group = RemovableGroup;
                        item.SubItems.Add(Language.drivename_removable);
                    }
                    else if (drive.Is(DriveFlags.Fixed))
                    {
                        item.Group = FixedDriveGroup;
                        item.SubItems.Add(Language.drivename_fixed);
                    }
                    else if (drive.Is(DriveFlags.CDRom))
                    {
                        item.Group = RemovableGroup;
                        item.SubItems.Add(Language.drivename_optical);
                    }
                    else if (drive.Is(DriveFlags.Network))
                    {
                        item.Group = NetworkGroup;
                        item.SubItems.Add(Language.drivename_network);
                    }
                    else
                    {
                        item.Group = UnknownGroup;
                        item.SubItems.Add(Language.drivename_unknown);
                    }

                    item.SubItems.Add(""); //Sem região
                    item.SubItems.Add(drive.Ready ? FormatFileSizeEbi(drive.FreeSpace, 1) + ' ' + Language.explorer_drive_freespace + ' ' +
                        FormatFileSizeEbi(drive.Size, 1) : "");

                    listView_explorer.Items.Add(item);
                }
            }

            listView_explorer.ResumeLayout();
            label_empty_folder.Visible = (listView_explorer.Items.Count == 0) && !configurationPanel_configuration.Visible;
        }

        //------------------------ Carrega a Biblioteca de jogos
        void LoadLibrary()
        {
            ExplorerPath = path_library;
            listView_explorer.SuspendLayout();
            listView_explorer.SuspendCoverMode = false;
            listView_explorer.ForceFullSize = false;

            foreach (ListViewItem k in listView_explorer.SelectedItems) k.Selected = false;

            listView_explorer.Items.Clear();
            label_empty_folder.Visible = false;

            SetPasteState(Library.Folders.Count > 0);

            if (!Library.ValidateFolders()) SetupQuickLinks();
            Library.ValidateFiles();

            if(EnablePathChangeSound)
                PathChangeSound.Play();

            for (int i = 0; i < Library.Folders.Count; i++)
            {
                LibraryFolder folder = Library.Folders[i];
                String text = Path.GetFileName(folder.Path);
                ListViewItem item = new ListViewItem();
                item.Text = (text.Length == 0 ? folder.Path : text);
                item.ToolTipText = item.Text;
                item.Tag = new TagMetadata(folder.Path, SelectionType.Folder, null);
                item.Group = FolderGroup;
                item.SubItems.Add(Language.explorer_folder);

                listView_explorer.Items.Add(item);
            }

            for (int i = 0; i < Library.Files.Count; i++)
            {
                LibraryFile file = Library.Files[i];
                ListViewItem item;
                String ssize;

                switch (file.Type)
                {
                    case LibraryFileType.Iso:

                        item = new ListViewItem();
                        item.Text = file.Name;
                        item.Group = WiiDiscGroup;
                        item.ToolTipText = file.Path;
                        item.Tag = new TagMetadata(file.Path, SelectionType.Disc, file.Region, (byte)0x1);

                        item.SubItems.Add(file.Code);
                        item.SubItems.Add(FormatDiscRegion(file.Region));

                        ssize = FormatFileSizeEbi(file.FileSize) + '/' + FormatFileSizeEbi(file.WBFSSize);
                        item.SubItems.Add(ssize);

                        listView_explorer.Items.Add(item);

                        break;

                    case LibraryFileType.Scrub:

                        item = new ListViewItem();
                        item.Text = file.Name;
                        item.Group = ScrubbedDiscGroup;
                        item.ToolTipText = file.Path;
                        item.Tag = new TagMetadata(file.Path, SelectionType.Disc, file.Region, (byte)0x2);

                        item.SubItems.Add(file.Code);
                        item.SubItems.Add(FormatDiscRegion(file.Region));

                        ssize = FormatFileSizeEbi(file.FileSize) + '/' + FormatFileSizeEbi(file.WBFSSize) + '/' + 
                            FormatFileSizeEbi(file.ScrubFullSize);
                        item.SubItems.Add(ssize);

                        listView_explorer.Items.Add(item);

                        break;
                }
            }

            listView_explorer.ResumeLayout();
            label_empty_folder.Visible = (listView_explorer.Items.Count == 0) && !configurationPanel_configuration.Visible;
        }

        //------------------------ Carrega a Lista de Capas
        void LoadCovers()
        {
            ExplorerPath = path_covers;
            listView_explorer.SuspendLayout();
            listView_explorer.SuspendCoverMode = false;
            listView_explorer.ForceFullSize = true;

            foreach (ListViewItem k in listView_explorer.SelectedItems) k.Selected = false;

            listView_explorer.Items.Clear();
            label_empty_folder.Visible = false;

            SetPasteState(false); //Nada de colar coisas em pastas especiais...

            if (EnablePathChangeSound)
                PathChangeSound.Play();

            String[] covers = Directory.GetFiles(Covers.CoversDir, "*.png");
            for (int i = 0; i < covers.Length; i++)
            {
                string c = Path.GetFileNameWithoutExtension(covers[i]);
                ListViewItem item = new ListViewItem(c);
                item.Tag = new TagMetadata(c, SelectionType.Covers, null, null);

                listView_explorer.Items.Add(item);
            }

            listView_explorer.ResumeLayout();
            label_empty_folder.Visible = (listView_explorer.Items.Count == 0) && !configurationPanel_configuration.Visible;
        }

        //------------------------ Carrega uma pasta qualquer não WBFS
        void LoadFolder(String path)
        {
            if (!ValidadeFolder(path)) return;

            ExplorerPath = path;
            listView_explorer.SuspendLayout();
            listView_explorer.SuspendCoverMode = false;

            foreach (ListViewItem k in listView_explorer.SelectedItems) k.Selected = false;

            listView_explorer.Items.Clear();
            listView_explorer.ForceFullSize = false;
            label_empty_folder.Visible = false;

            if(EnablePathChangeSound)
                PathChangeSound.Play();

            SetPasteState(true);

            //
            String[] objects = Directory.GetDirectories(path);
            for (int i = 0; i < objects.Length; i++)
            {
                ListViewItem item = new ListViewItem();
                item.Text = Path.GetFileName(objects[i]);
                item.ToolTipText = item.Text;
                item.Tag = new TagMetadata(objects[i], SelectionType.Folder, null);
                item.Group = FolderGroup;
                item.SubItems.Add(Language.explorer_folder);

                listView_explorer.Items.Add(item);
            }

            //
            objects = Directory.GetFiles(path, WiiDisc.typefilter_wiiiso);
            for (int i = 0; i < objects.Length; i++)
            {
                //Previne mensagens e mais mensagens de erro no Log por não conseguir travar o Contexto
                if (IOManager.ContainsHandle(objects[i])) continue;

                String name, code;
                int region;
                long size;

                if (WiiDisc.GetIsoInformation(objects[i], out code, out name, out region, out size) == 0) //Só discos de wii
                {
                    ListViewItem item = new ListViewItem();
                    item.Text = name;
                    item.Group = WiiDiscGroup;
                    item.ToolTipText = objects[i];
                    item.Tag = new TagMetadata(objects[i], SelectionType.Disc, (DiscRegion)region, (byte)0x1); // 1 pra isos

                    item.SubItems.Add(code);
                    item.SubItems.Add(FormatDiscRegion(region));

                    String ssize = FormatFileSizeEbi((new FileInfo(objects[i]).Length)) + '/' +
                        FormatFileSizeEbi(size * WBFSDevice.wiiSectorSize);

                    item.SubItems.Add(ssize);

                    listView_explorer.Items.Add(item);
                    Covers.SynchronizeCover(code, false);
                }
            }

            //
            objects = Directory.GetFiles(path, ScrubDisc.typefilter_wiiscrub);
            for (int i = 0; i < objects.Length; i++)
            {
                //Previne mensagens e mais mensagens de erro no Log por não conseguir travar o Contexto
                if (IOManager.ContainsHandle(objects[i])) continue;
                
                String name, code;
                int region;
                long size;
                long fullsize;

                if (ScrubDisc.GetScrubbedInfo(objects[i], out code, out name, out region, out fullsize, out size) == 0)
                {
                    ListViewItem item = new ListViewItem();
                    item.Text = name;
                    item.Group = ScrubbedDiscGroup;
                    item.ToolTipText = objects[i];
                    item.Tag = new TagMetadata(objects[i], SelectionType.Disc, (DiscRegion)region, (byte)0x2); // 2 para scrubs

                    item.SubItems.Add(code);
                    item.SubItems.Add(FormatDiscRegion(region));

                    String ssize = FormatFileSizeEbi((new FileInfo(objects[i]).Length)) + '/' +
                        FormatFileSizeEbi(size * WBFSDevice.wiiSectorSize) + '/' + FormatFileSizeEbi(fullsize);

                    item.SubItems.Add(ssize);

                    listView_explorer.Items.Add(item);
                    Covers.SynchronizeCover(code, false);
                }
            }

            listView_explorer.ResumeLayout();
            label_empty_folder.Visible = (listView_explorer.Items.Count == 0) && !configurationPanel_configuration.Visible;
        }

        //------------------------ Carrega um drive não WBFS, fazendo checagem de existencia
        public void LoadWinDrive(IDrive drive)
        {
            if (!ValidadeDrive(drive)) return;
            LoadFolder(drive.Name);
        }

        //------------------------ Carrega um drive WBFS
        public void LoadWBFSDrive(String Name)
        {
            LoadWBFSDrive((WBFSDrive)DeviceManager.GetDrive(Name));
        }

        //------------------------ Carrega um drive WBFS
        public void LoadWBFSDrive(WBFSDrive drive)
        {
            if (!ValidadeDrive(drive)) return;

            ExplorerPath = drive.Name;
            listView_explorer.SuspendLayout();
            listView_explorer.SuspendCoverMode = false;
            listView_explorer.ForceFullSize = false;

            foreach (ListViewItem k in listView_explorer.SelectedItems) k.Selected = false;

            listView_explorer.Items.Clear();
            label_empty_folder.Visible = false;

            SetPasteState(true);

            if(EnablePathChangeSound)
                PathChangeSound.Play();

            WBFSDevice device = drive._Device;
            IDisc disc = null;
            int i = 0;

            while ((disc = device.GetDiscByIndex(i++)) != null)
            {
                ListViewItem item = new ListViewItem();
                item.Text = disc.Name;
                item.Group = WiiDiscGroup;
                item.ToolTipText = item.Text;
                item.Tag = new TagMetadata(drive.Name + disc.Code, SelectionType.Disc, disc.Region, (byte)0x1);

                item.SubItems.Add(disc.Code);
                item.SubItems.Add(FormatDiscRegion((int)disc.Region));

                String ssize = FormatFileSizeEbi(disc.Size) + " / " +
                    FormatFileSizeEbi(disc.WBFSSize);

                item.SubItems.Add(ssize);

                listView_explorer.Items.Add(item);

                Covers.SynchronizeCover(disc.Code, false);
            }

            listView_explorer.ResumeLayout();
            label_empty_folder.Visible = (listView_explorer.Items.Count == 0) && !configurationPanel_configuration.Visible;
        }

        private void toolStripMenuItem_organize_config_Click(object sender, EventArgs e)
        {
            label_empty_folder.Visible = false;
            configurationPanel_configuration.SetControls();
            configurationPanel_configuration.Visible = true;
        }

        private void toolStripMenuItem_organize_close_Click(object sender, EventArgs e)
        {
            Close();
        }

        private void toolStripMenuItem_organize_donate_Click(object sender, EventArgs e)
        {
            Process.Start(@"http://wbfssync.codeplex.com");
        }

        private void toolStripMenuItem_organize_about_Click(object sender, EventArgs e)
        {
            Process.Start(@"http://wbfssync.codeplex.com");
        }

        private void toolStripMenuItem_organize_help_Click(object sender, EventArgs e)
        {
            Process.Start(@"http://wbfssync.codeplex.com");
        }

        private void toolStripMenuItem_explorer_downloadcover_Click(object sender, EventArgs e)
        {
            foreach (ListViewItem i in listView_explorer.SelectedItems)
            {
                if (i.SubItems.Count >= 1)
                    Covers.SynchronizeCover(i.SubItems[1].Text, true);
            }
        }

    }
}
